﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ApiDoc
{
    /// <summary>
    /// Api Documentation for a Method.
    /// </summary>
    public class MethodDocumentation : MemberDocumentationBase<MethodInfo>
    {
        internal MethodDocumentation(MethodInfo member, ApiDocumentationManager manager)
            : base(member, manager)
        {
        }


        public string Name
        {
            get
            {
                return Manager.CodeSyntaxGenerator.GenerateName(Member);
            }
        }

        public string CodeDefinition
        {
            get
            {
                return Manager.CodeSyntaxGenerator.GenerateCodeDefinition(Member);
            }
        }

        /// <summary>
        /// Gets the basic modifiers.
        /// </summary>
        public string Modifiers
        {
            get
            {
                if ((Member.Attributes & MethodAttributes.Public) != 0) {
                    if (Member.IsStatic) {
                        return "public static";
                    } else {
                        return "public";
                    }
                } else if ((Member.Attributes & MethodAttributes.Assembly) != 0) {
                    if (Member.IsStatic) {
                        return "internal static";
                    } else {
                        return "internal";
                    }
                } else if ((Member.Attributes & MethodAttributes.FamANDAssem) != 0) {
                    if (Member.IsStatic) {
                        return "protected internal static";
                    } else {
                        return "protected internal";
                    }
                } else if ((Member.Attributes & MethodAttributes.FamORAssem) != 0) {
                    if (Member.IsStatic) {
                        return "protected internal static";
                    } else {
                        return "protected internal";
                    }
                } else if ((Member.Attributes & MethodAttributes.Family) != 0) {
                    if (Member.IsStatic) {
                        return "protected static";
                    } else {
                        return "protected";
                    }
                } else if ((Member.Attributes & MethodAttributes.Private) != 0) {
                    if (Member.IsStatic) {
                        return "private static";
                    } else {
                        return "private";
                    }
                } else if ((Member.Attributes & MethodAttributes.PrivateScope) != 0) {
                    if (Member.IsStatic) {
                        return "private static";
                    } else {
                        return "private";
                    }
                }

                return String.Empty;
            }
        }

        /// <summary>
        /// Gets the Parameters of this Method.
        /// </summary>
        public IEnumerable<ParameterDocumentation> Parameters
        {
            get
            {
                if (m_parameters == null) {
                    m_parameters = Member.GetParameters()
                        .Cast<ParameterInfo>()
                        .Select(item => Manager.GetParameterDocumentation(item))
                        .OrderBy(item => item.Name)
                        .ToList();
                }

                return m_parameters;
            }
        }

        private IEnumerable<ParameterDocumentation> m_parameters;


        /// <summary>
        /// Generic Type arguments.
        /// </summary>
        public IEnumerable<TypeDocumentation> GenericTypeArguments
        {
            get
            {
                if (m_genericTypeArguments == null) {
                    var ret = new List<TypeDocumentation>();

                    var arguments = Member.GetGenericArguments();
                    foreach (var argument in arguments) {
                        ret.Add(new TypeDocumentation(argument, Manager)); // NOTE we don't use the managers cache here as we wan't to ignore the cache.
                    }

                    m_genericTypeArguments = ret;
                }

                return m_genericTypeArguments;
            }
        }

        private IEnumerable<TypeDocumentation> m_genericTypeArguments;

        /// <summary>
        /// Gets the parent type documentation this member is contained within.
        /// </summary>
        public TypeDocumentation ParentType
        {
            get
            {
                if (m_parentType == null) {
                    if (Member.ReflectedType == null) {
                        return null;
                    }

                    m_parentType = Manager.GetTypeDocumentation(Member.ReflectedType);
                }

                return m_parentType;
            }
        }

        private TypeDocumentation m_parentType;

        /// <summary>
        /// Gets the type documentation for the type this member was declared within.
        /// </summary>
        /// <remarks>
        /// This is the class the developer actually wrote the code of this method within.
        /// </remarks>
        public TypeDocumentation DeclaringType
        {
            get
            {
                if (m_declaringType == null) {
                    if (Member.DeclaringType == null) {
                        return null;
                    }

                    m_declaringType = Manager.GetTypeDocumentation(Member.DeclaringType);
                }

                return m_declaringType;
            }
        }

        private TypeDocumentation m_declaringType;

        /// <summary>
        /// Gets the type documentation for the return type this of this member.
        /// </summary>
        public TypeDocumentation ReturnType
        {
            get
            {
                if (m_returnType == null) {
                    if (Member.ReturnType == null) {
                        return null;
                    }

                    m_returnType = Manager.GetTypeDocumentation(Member.ReturnType);
                }

                return m_returnType;
            }
        }

        private TypeDocumentation m_returnType;
    }
}
